/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.inspur.edp.lcm.metadata.core;

import com.inspur.edp.lcm.metadata.api.IMdExtRuleContent;
import com.inspur.edp.lcm.metadata.api.entity.*;
import com.inspur.edp.lcm.metadata.api.entity.MetadataIndexItemDto;
import com.inspur.edp.lcm.metadata.api.entity.metadataindex.MetadataIndexDto;
import com.inspur.edp.lcm.metadata.api.exception.ErrorCodes;
import com.inspur.edp.lcm.metadata.api.exception.LcmMetadataDevException;
import com.inspur.edp.lcm.metadata.api.exception.LcmProcessException;
import com.inspur.edp.lcm.metadata.api.service.GspProjectService;
import com.inspur.edp.lcm.metadata.api.service.MetadataService;
import com.inspur.edp.lcm.metadata.common.FileServiceImp;
import com.inspur.edp.lcm.metadata.common.Utils;
import com.inspur.edp.lcm.metadata.common.util.MetadataSelectorUtils;
import com.inspur.edp.lcm.metadata.core.persistence.RepositoryFactory;
import com.inspur.edp.lcm.metadata.devcommon.ManagerUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.util.StringUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author zhaoleitr
 */
@Slf4j
public class MetadataServiceImp implements MetadataService {
	private final GspProjectService gspProjectService = new GspProjectServiceImp();
	private final MetadataProjectCoreService metadataProjectService = MetadataProjectCoreService.getCurrent();
	private final MetadataCoreManager metadataCoreManager = MetadataCoreManager.getCurrent();
	public static final String UTF8_BOM = "\uFEFF";
	@Override
	public GspMetadata initializeMetadataEntity(GspMetadata metadata) {
		metadataCoreManager.initializeMetadataEntity(metadata);
		return metadata;
	}

	@Override
	public void createMetadata(String path, GspMetadata metadata) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		metadataCoreManager.createMetadata(absolutePath, metadata);
	}

	@Override
	public GspMetadata loadMetadata(String metadataFileName, String path) {
		Utils.checkEmpty(metadataFileName, "parameter value is empty,paramter is metadataFileName");
		Utils.checkEmpty(path, "parameter value is empty,paramter is path");
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		GspMetadata metadata = metadataCoreManager.loadMetadata(metadataFileName, absolutePath);
		metadata.setRelativePath(ManagerUtils.getRelativePath(metadata.getRelativePath()));
		return metadata;
	}

	@Override
	public GspMetadata loadMetadata(String metadataPath) {
		Utils.checkEmpty(metadataPath, "parameter value is empty,paramter is metadataPath");
		String absolutePath = ManagerUtils.getAbsolutePath(metadataPath);
		GspMetadata metadata = metadataCoreManager.loadMetadata(absolutePath);
		metadata.setRelativePath(ManagerUtils.getRelativePath(metadata.getRelativePath()));
		return metadata;
	}

	@Override
	public GspMetadata loadMetadataByMetadataId(String metadataId, String path) {
		Utils.checkEmpty(metadataId, "parameter value is empty,paramter is metadataFileName");
		Utils.checkEmpty(path, "parameter value is empty,paramter is path");
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		return metadataCoreManager.loadMetadataByMetadataId(metadataId, absolutePath);
	}

	@Override
	public GspMetadata loadMetadataInBo(String metadataFullPath, String currentProjPath) {
		String absoluteMetadataPath = ManagerUtils.getAbsolutePath(metadataFullPath);
		String absoluteProjPath = ManagerUtils.getAbsolutePath(currentProjPath);
		GspMetadata metadata = metadataCoreManager.loadMetadataInBo(absoluteMetadataPath, absoluteProjPath);
		metadata.setRelativePath(ManagerUtils.getRelativePath(metadata.getRelativePath()));
		return metadata;
	}

	@Override
	public GspMetadata getRefMetadata(String spacePath, String metadataID) {
		if (!spacePath.isEmpty()) {
			spacePath = ManagerUtils.getAbsolutePath(spacePath);
		}
		return metadataCoreManager.getRefMetadata(metadataID, spacePath);
	}

	@Override
	public GspMetadata getMetadataWithoutContent(String fullPath) {
		String absolutePath = ManagerUtils.getAbsolutePath(fullPath);
		GspMetadata metadata = metadataCoreManager.getMetadataWithoutContent(absolutePath);
		metadata.setRelativePath(ManagerUtils.getRelativePath(metadata.getRelativePath()));
		return metadata;
	}

	@Override
	public void deleteMetadata(String path, String metadataFileName) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		metadataCoreManager.deleteMetadata(absolutePath, metadataFileName);
	}

	@Override
	public void deleteFolder(String path) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		// 删除前，先检查元数据是否被引用
		isMetadataRefedForDir(absolutePath);
		metadataCoreManager.deleteFolder(absolutePath);
	}

	@Override
	public void renameMetadata(String oldFileName, String newFileName, String metadataFilePath) {
		String absolutePath = ManagerUtils.getAbsolutePath(metadataFilePath);
		metadataCoreManager.renameMetadata(oldFileName, newFileName, absolutePath);
	}

	@Override
	public List<GspMetadata> getMetadataList(String spacePath, List<String> metadataTypes) {
		String absolutePath = ManagerUtils.getAbsolutePath(spacePath);
		List<GspMetadata> result = metadataCoreManager.getMetadataList(absolutePath, metadataTypes);
		return result;
	}

	@Override
	public List<MetadataDto> getMetadataListInBo(String path, List<String> metadataTypes) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		List<MetadataDto> metadataListInBo = metadataCoreManager.getMetadataListInBo(metadataProjectService.getProjPath(absolutePath), metadataTypes);
		return metadataListInBo;
	}

	@Override
	public List<MetadataDto> getMetadataListInBoForIDE(String path, List<String> metadataTypes, String searchText) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		return metadataCoreManager.getMetadataListInBoByBoPath(absolutePath, metadataTypes, searchText);
	}

	@Override
	public List<GspMetadata> getMetadataByFilter(String path, MetadataFilter metadataFilter) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		List<GspMetadata> metadataByFilter = metadataCoreManager.getMetadataByFilter(absolutePath, metadataFilter);
		return metadataByFilter;
	}

	@Override
	public boolean validateRepeatName(String path, String fileName) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		return metadataCoreManager.validateRepeatName(absolutePath, fileName);
	}

	@Override
	public List<MetadataType> getMetadataTypeList() {
		return metadataCoreManager.getMetadataTypeList();
	}

	@Override
	public boolean isMetadataExist(String path, String metadataFileName) {
		String absolutePath = Paths.get(ManagerUtils.getAbsolutePath(path)).resolve(metadataFileName).toString();
		return metadataCoreManager.isMetadataExist(absolutePath);
	}

	@Override
	public boolean isMetadataExistInProject(String path, String metadataID) {
		String projPath = metadataProjectService.getProjPath(ManagerUtils.getAbsolutePath(path));
		return metadataCoreManager.isMetadataExistInProject(projPath, metadataID);
	}

	@Override
	public GspMetadata getMetadataInBoByMetadataID(String path, String metadataID) {
		// 参数校验
		if (!StringUtils.hasText(path)) {
			throw new LcmMetadataDevException(ErrorCodes.ECP_METADATA_DEV_0005, "path");
		}
		if (!StringUtils.hasText(metadataID)) {
			throw new LcmMetadataDevException(ErrorCodes.ECP_METADATA_DEV_0005, "metadataID");
		}
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		String projPath = metadataProjectService.getProjPath(absolutePath);
		if (!StringUtils.hasText(projPath)) {
			throw new LcmMetadataDevException(ErrorCodes.ECP_METADATA_DEV_0045, absolutePath);
		}
		List<String> projectsInBo = metadataProjectService.getProjectsInBoByProjPath(projPath);
		GspMetadata gspMetadata = null;
		for (String projectPath : projectsInBo) {
			gspMetadata = metadataCoreManager.getMetadataWithContentInProjectByID(projectPath, metadataID);
			if (gspMetadata != null) {
				break;
			}
		}
		return gspMetadata;
	}

	@Override
	public String getRefPath(String path) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		return metadataCoreManager.getRefPath(absolutePath);
	}

	@Override
	public MetadataHeader getRefMetadataHeader(String metadataID, String path) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		return metadataCoreManager.getRefMetadataHeader(metadataID, absolutePath);
	}

	@Override
	public List<GspMetadata> getMetadataList(String spacePath) {
		String absolutePath = ManagerUtils.getAbsolutePath(spacePath);
		List<GspMetadata> result = metadataCoreManager.getMetadataList(absolutePath);
		return result;
	}

	@Override
	public MetadataPackage getMetadataPackageInfo(String packageFileName, String packagePath) {
		if (packagePath.isEmpty()) {
			packagePath = ManagerUtils.getMetadataPackageLocation();
		}

		return metadataCoreManager.getMetadataPackageInfo(packageFileName, packagePath);
	}

	@Override
	public List<GspMetadata> getMetadataListByRefedMetadataId(String path, String metadataId) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		String metadataPath = new FileServiceImp().getCombinePath(metadataProjectService.getProjPath(absolutePath), Utils.getMetadataProjPath());
		return metadataCoreManager.getMetadataListByRefedMetadataId(metadataPath, metadataId);
	}

	@Override
	public Metadata4Ref getMetadataFromPackage(String packageName, String packagePath, String metadataID) {

		if (packagePath.isEmpty()) {
			packagePath = ManagerUtils.getMetadataPackageLocation();
		}
		return metadataCoreManager.getMetadataFromPackage(packageName, packagePath, metadataID);
	}

	@Override
	public MetadataProject getMetadataProjInfo(String path) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		return metadataCoreManager.getMetadataProjInfo(absolutePath);
	}

	@Override
	public List<MetadataIndexItemDto> getRemoteMetadataIndexList(String text, String path, String selectedPackageSource, List<String> typeList, boolean page, int pageSize, int pageIndex, boolean isFilterByRefs, boolean conflictAvoidFlag) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		return metadataCoreManager.getRemoteMetadataIndexList(text, absolutePath, selectedPackageSource, typeList, page, pageSize, pageIndex, isFilterByRefs, conflictAvoidFlag);
	}

	@Override
	public List<MetadataIndexItemDto> getLocalMetadataList(String text, String path, List<String> typeList, boolean page, int pageSize, int pageIndex) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		return metadataCoreManager.getLocalMetadataList(text, absolutePath, typeList, page, pageSize, pageIndex, ManagerUtils.getMetadataPackageLocation());
	}

	@Override
	public List<MetadataIndexItemDto> getDBMetadataList(String text, String path, List<String> typeList, boolean page, int pageSize, int pageIndex) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		return metadataCoreManager.getDBMetadataList(text, absolutePath, typeList, page, pageSize, pageIndex);
	}
	@Override
	public MetadataIndexDto getUnionMetadataList(String text, String path, List<String> typeList, int pageSize, int pageIndex) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		MetadataIndexDto metadataIndexDto;
		if (MetadataSelectorUtils.isBackendPaginationEnabled()) {
			metadataIndexDto = metadataCoreManager.getUnionMetadataListByPage(text, absolutePath, typeList, pageSize, pageIndex);
		} else {
			metadataIndexDto = metadataCoreManager.getUnionMetadataList(text, absolutePath, typeList, pageSize, pageIndex);
		}
		metadataIndexDto.getMetadataIndexItems().forEach(indexItem -> indexItem.setRelativePath(ManagerUtils.getRelativePath(indexItem.getRelativePath())));
		return metadataIndexDto;
	}

	@Override
	public Metadata4RefDto pickMetadataIndex(int scopeType, String currentPath, MetadataIndexItemDto metadataIndexItemDto, boolean isNeedMdDto) {
		String absolutePath = ManagerUtils.getAbsolutePath(currentPath);
		if (scopeType == 1 || scopeType == 2 || scopeType == 4) {
			absolutePath = metadataProjectService.getProjPath(absolutePath);
		}
		return metadataCoreManager.pickMetadataIndex(scopeType, absolutePath, metadataIndexItemDto, isNeedMdDto, ManagerUtils.getMetadataPackageLocation(), ManagerUtils.getMavenStoragePath());
	}
	@Override
	public Metadata4RefDto pickMetadata(String currentPath, MetadataIndexItemDto metadataIndexItemDto) {

		String absolutePath = metadataProjectService.getProjPath(ManagerUtils.getAbsolutePath(currentPath));
		return metadataCoreManager.pickMetadata( absolutePath, metadataIndexItemDto);
	}

	@Override
	public GspProject getGspProjectInfo(String path) {
		try {
			return gspProjectService.getGspProjectInfo(path);
		} catch (IOException e) {
			log.error("获取工程信息失败，请检查工程路径", e);
		}
		return null;
	}

	@Override
	public List<MetadataHeader> getResourceMetadata(String metadataId, String path) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		MetadataProject projInfo = metadataProjectService.getMetadataProjInfo(absolutePath);
		return metadataCoreManager.getResourceMetadata(metadataId, projInfo.getProjectPath());
	}

	@Override
	public void setMetadataUri(String path) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		metadataCoreManager.setMetadataUri(absolutePath);
	}

	@Override
	public GspMetadata getI18nMetadata(String fileName, String path, String language) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		return metadataCoreManager.getI18nMetadata(fileName, absolutePath, language);
	}

	@Override
	public List<GspMetadata> GetRefI18nMetadata(String resourceMdID, String resourceFileName, String path) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		return metadataCoreManager.getRefI18nMetadata(resourceMdID, resourceFileName, absolutePath);
	}

	@Override
	public void saveMetadata(GspMetadata metadata, String fullPath) {
		MetadataCoreManager.devRootPath.set(ManagerUtils.getDevRootPath());
		String absolutePath = ManagerUtils.getAbsolutePath(fullPath);
		metadataCoreManager.saveMetadata(metadata, absolutePath);
	}

	@Override
	public String getIndexServerStatus(String ip, String port) {
		return metadataCoreManager.getIndexServerStatus(ip, port);
	}

	@Override
	public IMdExtRuleContent getMdExtRule(String metadataId, String path) {
		GspMetadata metadata = loadMetadataByMetadataId(metadataId, path);
		if (!Objects.isNull(metadata) ) {
			return metadata.getExtendRule();
		}
		return null;
	}

	@Override
	public void saveMdExtRule(String metadataId, String path, IMdExtRuleContent extendRule) {
		try {
			GspMetadata metadata = loadMetadataByMetadataId(metadataId, path);
			if (Objects.isNull(metadata)) {
				throw new LcmMetadataDevException(ErrorCodes.ECP_METADATA_DEV_0035, metadataId, path);
			}
			metadata.setExtendRule(extendRule);
			saveMetadata(metadata, path);
		}
		catch (Exception e){
			throw new LcmMetadataDevException(e, ErrorCodes.ECP_METADATA_DEV_0036, metadataId, path);
		}
	}

	@Override
	public boolean isMetadataRefed(String path) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		return metadataCoreManager.isMetadataRefed(absolutePath);
	}

	@Override
	public String mdfileSha1(String path) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		return DigestUtils.sha1Hex(mdfileRead(absolutePath));
	}

	@Override
	public List<MetadataDto> getMetadataListInSu(String path, List<String> metadataTypes) {
		String absolutePath = ManagerUtils.getAbsolutePath(path);
		List<MetadataDto> metadataListInSu = metadataCoreManager.getMetadataListInSu(metadataProjectService.getProjPath(absolutePath), metadataTypes);
		return metadataListInSu;
	}

	@Override
	public List<MetadataIndexItemDto> getRecentMdUseList(String appId,String path, List<String> typeList) {
		String absolutePath = StringUtils.hasText(path)?ManagerUtils.getAbsolutePath(path):null;
		return metadataCoreManager.getRecentMdUseList(appId,absolutePath,typeList);
	}

	private boolean isMetadataRefedForDir(String dirPath) {
		String absolutePath = ManagerUtils.getAbsolutePath(dirPath);
		List<String> paths = new ArrayList<>();
		RepositoryFactory.getInstance().getMetadataRepository().getMetadatasUnderDir(paths, absolutePath);
		List<String> metadataAllTypes = Utils.getMetadataPostfixTypes();
		FileServiceImp fileService = new FileServiceImp();
		for (String filePath : paths) {
			String extension = fileService.getExtension(filePath).toLowerCase();
			String result = metadataAllTypes.stream().filter(item -> item.toLowerCase().equals(extension)).findAny().orElse(null);
			if (result != null) {
				if(isMetadataRefed(filePath)){
					throw new LcmMetadataDevException(ErrorCodes.ECP_METADATA_DEV_0037, filePath);
				}
			}
		}
		return false;
	}
	private String mdfileRead(String path) {
		InputStream is = null;
		InputStreamReader isr = null;
		BufferedReader br = null;
		String s = null;
		try {
			is = new FileInputStream(path);
			isr = new InputStreamReader(is, StandardCharsets.UTF_8);
			br = new BufferedReader(isr);

			StringBuilder sb = new StringBuilder();
			String line;
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}
			s = sb.toString();
			if (s.startsWith(UTF8_BOM)) {
				s = s.substring(1);
			}
		} catch (IOException e) {
			throw new LcmProcessException(e, ErrorCodes.ECP_FILE_DEV_0006,path);
		}finally {
			try {
				if (br != null) {
					br.close();
				}
				if (isr != null) {
					isr.close();
				}
				if (is != null) {
					is.close();
				}
			} catch (IOException e) {
				log.warn("关闭文件流失败：" + path);
			}
		}
		return s;
	}
}
